﻿using Sayook.Framework.DbCore.DbException;
using Sayook.Framework.DbCore.SqlModel;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;

namespace Sayook.Framework.DbCore.DbContext
{
    public static class DbContextExtensions
    {
        /// <summary>
        /// SELECT 字句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <returns>SELECT * FROM 表名</returns>
        public static BaseDbContext<T> Select<T>([NotNull] this BaseDbContext<T> context) where T : DbModelBase
        {
            context.SqlString.Select = context.SqlBuilder.CreateSelect<T>();
            return context;
        }

        /// <summary>
        /// SELECT 字句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">查询字段表达式 eg:x => new { x.Id, x.Name, ...}</param>
        /// <returns>SELECT filed1,filed2,... FROM 表名</returns>
        public static BaseDbContext<T> Select<T>([NotNull] this BaseDbContext<T> context, [NotNull] Expression<Func<T, object>> expression) where T : DbModelBase
        {
            context.SqlString.Select = context.SqlBuilder.CreateSelect(expression);
            return context;
        }

        /// <summary>
        /// SELECT DISTINCT 语句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">查询字段表达式 eg:x => x.Name</param>
        /// <remarks>
        /// DISTINCT在查询一个字段或者很少字段的情况下使用,会避免重复数据的出现,给查询带来优化效果。但是查询字段很多的情况下使用，则会大大降低查询效率
        /// 所以 SelectDistinct()目前只开放查询一个字段,超出将抛出异常!
        /// </remarks>
        /// <returns>SELECT DISTINCT 字段名 FROM 表名</returns>
        public static BaseDbContext<T> SelectDistinct<T, TElement>([NotNull] this BaseDbContext<T> context, [NotNull] Expression<Func<T, TElement>> expression) where T : DbModelBase
        {
            context.SqlString.Select = context.SqlBuilder.CreateSelectDistinct(expression);
            return context;
        }

        /// <summary>
        /// WHERE 字句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">条件字段表达式 eg:x => SqlWhere.Where(x.Id, SqlOperator.Equal)</param>
        /// <returns>WHERE field = @field</returns>
        public static BaseDbContext<T> Where<T>([NotNull] this BaseDbContext<T> context, [NotNull] Expression<Func<T, SqlWhere>> expression) where T : DbModelBase
        {
            if (context.SqlString.Where?.IndexOf(SqlKeyWord.Where.Trim()) >= 0)
            {
                throw new CreateSqlException("SQL语句已存在WHERE关键字,考虑使用 And 或者 Or");
            }
            context.SqlString.Where = context.SqlBuilder.CreateWhere(expression);
            return context;
        }

        /// <summary>
        /// WHERE 字句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">条件字段表达式 eg:x => SqlWhere.Where(x.Id, SqlOperator.Equal)</param>
        /// <returns>WHERE field = @field</returns>
        public static BaseDbContext<T> Where<T, TElement>([NotNull] this BaseDbContext<T> context, [NotNull] Expression<Func<T, TElement>> expression, SqlOperator sqlOperator) where T : DbModelBase
        {
            if (context.SqlString.Where?.IndexOf(SqlKeyWord.Where.Trim()) >= 0)
            {
                throw new CreateSqlException("SQL语句已存在WHERE关键字,考虑使用 And 或者 Or");
            }
            context.SqlString.Where = context.SqlBuilder.CreateWhere(expression);
            return context;
        }

        /// <summary>
        /// WHERE 字句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <typeparam name="TValue">参数类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">条件字段表达式 eg:x => SqlWhere.Where(x.Id, SqlOperator.Equal)</param>
        /// <param name="value">条件字段的值</param>
        /// <returns>WHERE field = value</returns>
        public static BaseDbContext<T> Where<T, TElement, TValue>([NotNull] this BaseDbContext<T> context, [NotNull] Expression<Func<T, TElement>> expression, SqlOperator sqlOperator, TValue value) where T : DbModelBase
        {
            if (context.SqlString.Where?.IndexOf(SqlKeyWord.Where.Trim()) >= 0)
            {
                throw new CreateSqlException("SQL语句已存在WHERE关键字,考虑使用 And 或者 Or");
            }
            context.SqlString.Where = context.SqlBuilder.CreateWhere(expression, value);
            return context;
        }

        /// <summary>
        /// AND 字句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">条件字段表达式 eg:x => SqlWhere.Where(x.Name, SqlOperator.Equal)</param>
        /// <returns>AND field = @field</returns>
        public static BaseDbContext<T> And<T>([NotNull] this BaseDbContext<T> context, [NotNull] Expression<Func<T, SqlWhere>> expression) where T : DbModelBase
        {
            if (context.SqlString.Where?.IndexOf(SqlKeyWord.Where.Trim()) < 0)
            {
                throw new CreateSqlException("SQL语句存在不存在WHERE关键字,请先使用 Where");
            }
            context.SqlString.Where += context.SqlBuilder.CreateAnd(expression);
            return context;
        }

        /// <summary>
        /// OR字句
        /// 谨慎使用Or字句,如果条件字段中,一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">条件字段表达式 eg:x => SqlWhere.Where(x.Id, SqlOperator.Equal)</param>
        /// <returns>OR field = @field</returns>
        public static BaseDbContext<T> Or<T>([NotNull] this BaseDbContext<T> context, [NotNull] Expression<Func<T, SqlWhere>> expression) where T : DbModelBase
        {
            if (context.SqlString.Where?.IndexOf(SqlKeyWord.Where.Trim()) < 0)
            {
                throw new CreateSqlException("SQL语句存在不存在WHERE关键字,请先使用 Where");
            }
            context.SqlString.Where += context.SqlBuilder.CreateOr(expression);
            return context;
        }

        /// <summary>
        /// ORDER BY 字句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <typeparam name="TElement">排序字段类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">ORDER BY表达式 eg:x => x.Id</param>
        /// <returns>ASC</returns>
        public static BaseDbContext<T> OrderBy<T, TElement>([NotNull] this BaseDbContext<T> context, [NotNull] Expression<Func<T, TElement>> expression) where T : DbModelBase
        {
            if (string.IsNullOrWhiteSpace(context.SqlString.OrderBy))
            {
                context.SqlString.OrderBy = SqlKeyWord.Ordeyby;
            }
            else
            {
                context.SqlString.OrderBy += ",";
            }
            context.SqlString.OrderBy += context.SqlBuilder.CreateOrderBy(expression);
            return context;
        }

        /// <summary>
        /// ORDER BY 字句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <typeparam name="TElement">排序字段类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">ORDER BY表达式 eg:x => x.Id</param>
        /// <returns>DESC</returns>
        public static BaseDbContext<T> OrderByDesc<T, TElement>([NotNull] this BaseDbContext<T> context, [NotNull] Expression<Func<T, TElement>> expression) where T : DbModelBase
        {
            if (string.IsNullOrWhiteSpace(context.SqlString.OrderBy))
            {
                context.SqlString.OrderBy = SqlKeyWord.Ordeyby;
            }
            else
            {
                context.SqlString.OrderBy += ",";
            }

            context.SqlString.OrderBy += context.SqlBuilder.CreateOrderByDesc(expression);
            return context;
        }

        /// <summary>
        /// 原生Sql查询
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="rawSql">原生Sql语句</param>
        /// <returns></returns>
        public static BaseDbContext<T> FromSqlRaw<T>([NotNull] this BaseDbContext<T> context, [NotNull] string rawSql) where T : DbModelBase
        {
            context.SqlString.Select = rawSql;
            return context;
        }

        /// <summary>
        /// 跳过[count]行
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="count">行数</param>
        /// <returns></returns>
        public static BaseDbContext<T> Skip<T>([NotNull] this BaseDbContext<T> context, int count) where T : DbModelBase
        {
            context.SqlString.SkipCount = count;
            context.SqlString.Paging = context.SqlBuilder.CreatePageing(count, context.SqlString.TakeCount);
            return context;
        }

        /// <summary>
        /// 取前[count]行
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="count">行数</param>
        /// <returns></returns>
        public static BaseDbContext<T> Take<T>([NotNull] this BaseDbContext<T> context, int count) where T : DbModelBase
        {
            context.SqlString.TakeCount = count;
            context.SqlString.Paging = context.SqlBuilder.CreatePageing(context.SqlString.SkipCount, count);
            return context;
        }

        /// <summary>
        /// UPDATE 语句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <typeparam name="TElement">更新字段的类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">更新字段的表达式 eg:x => x.Name | x => new { x.Name, x.Remark }</param>
        /// <returns></returns>
        public static BaseDbContext<T> Update<T, TElement>([NotNull] this BaseDbContext<T> context, [NotNull] Expression<Func<T, TElement>> expression) where T : DbModelBase
        {
            context.SqlString.Update = context.SqlBuilder.CreateUpdate(expression);
            return context;
        }

        /// <summary>
        /// UPDATE 语句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <typeparam name="TElement">更新字段的类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="data">修改的数据</param>
        /// <param name="expression">更新字段的表达式 eg:x => x.Name | x => new { x.Name, x.Remark }</param>
        /// <returns></returns>
        public static BaseDbContext<T> Update<T, TElement>([NotNull] this BaseDbContext<T> context, [NotNull] T data, [NotNull] Expression<Func<T, TElement>> expression) where T : DbModelBase
        {
            context.SqlString.Update = context.SqlBuilder.CreateUpdate(expression);
            context.Entity = data;
            return context;
        }

        /// <summary>
        /// 单个新增
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <typeparam name="TElement">新增记录的类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="Data">新增的数据</param>
        public static BaseDbContext<T> Insert<T>([NotNull] this BaseDbContext<T> context, T Data) where T : DbModelBase
        {
            context.SqlString.Insert = context.SqlBuilder.CreateInsert<T, object>(null);
            return context;
        }

        /// <summary>
        /// 单个新增
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <typeparam name="TElement">新增记录的类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="Data">新增的数据</param>
        /// <param name="expression">新增记录的表达式:x => new { x.Id, x.Name }</param>
        public static BaseDbContext<T> Insert<T, TElement>([NotNull] this BaseDbContext<T> context, T Data, Expression<Func<T, TElement>> expression) where T : DbModelBase
        {
            context.SqlString.Insert = context.SqlBuilder.CreateInsert(expression);
            return context;
        }

        /// <summary>
        /// 批量新增
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="Datas">新增的数据[不能超过500条]</param>
        public static BaseDbContext<T> Insert<T>([NotNull] this BaseDbContext<T> context, IEnumerable<T> Datas) where T : DbModelBase
        {
            context.SqlString.Insert = context.SqlBuilder.CreateInsert<T, object>(null);
            context.Entitys = Datas;
            return context;
        }

        /// <summary>
        /// 批量新增
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="Datas">新增的数据[不能超过500条]</param>
        /// <param name="expression">新增记录的表达式:x => new { x.Id, x.Name }</param>
        public static BaseDbContext<T> Insert<T, TElement>([NotNull] this BaseDbContext<T> context, IEnumerable<T> Datas, Expression<Func<T, TElement>> expression = null) where T : DbModelBase
        {
            context.SqlString.Insert = context.SqlBuilder.CreateInsert<T, object>(null);
            context.Entitys = Datas;
            return context;
        }

        /// <summary>
        /// DELETE 语句
        /// </summary>
        /// <typeparam name="T">数据库模型类型</typeparam>
        /// <param name="context">访问数据库对象</param>
        /// <param name="expression">条件字段表达式 eg:x => SqlWhere.Where(x.Id, SqlOperator.Less)</param>
        /// <returns></returns>
        public static BaseDbContext<T> Delete<T>([NotNull] this BaseDbContext<T> context) where T : DbModelBase
        {
            context.SqlString.Delete = context.SqlBuilder.CreateDelete<T>();
            return context;
        }
    }
}